package pnu.ssm.model.stuff;

import java.util.LinkedList;
import java.util.Queue;

import pnu.ssm.controller.manager.TileManager;
import pnu.ssm.model.GPosition;
import pnu.ssm.model.command.Command;
import pnu.ssm.model.job.Job;
import pnu.ssm.model.job.JobEnum;
import pnu.ssm.model.tile.Tile;
import pnu.ssm.model.tile.TileEnum;
import pnu.ssm.view.Dispatcher;

public class Transporter extends Stuff {
	private Tile[][] map;
	private int totalDis;
	private int emptyDis; 
	private int velocity; // additional item
	private Job job;
	private Block holding;
	private final GPosition[] wayAlgorithm = {
			new GPosition(-1, -1), // leftUp
			new GPosition(0, -1), // Up
			new GPosition(1,-1), //rightUp
			new GPosition(-1,0), // left
			new GPosition(1,0), // right
			new GPosition(-1,1), // leftDown
			new GPosition(0,1), // Down
			new GPosition(1,1) // rightDown
	};
	
	public Transporter(String name, GPosition pos, int velocity, Tile[][] map) {
		super(name, pos, TileEnum.TRANSPORTER);
		this.velocity = velocity;
		this.map = map;
		this.job = new Job(null, null, null);
		this.job.setState(JobEnum.Todo);
	}
	
	public void plusEmptyDis(int x, int y){
		emptyDis += (x+y) * TileManager.tileSize;
	}
	
	public void plusTotalDis(int x, int y){
		totalDis+= (x+y) * TileManager.tileSize;
	}
	
	public int getTotalDis() {
		return totalDis;
	}

	public void setTotalDis(int totalDis) {
		this.totalDis = totalDis;
	}

	public int getEmptyDis() {
		return emptyDis;
	}

	public void setEmptyDis(int emptyDis) {
		this.emptyDis = emptyDis;
	}

	@Override
	public int getDistance(Stuff otherStuff) {
		return 0;
	}

	public int getVelocity() {
		return velocity;
	}

	public void setVelocity(int velocity) {
		this.velocity = velocity;
	}
	
	public boolean isWorking(){
		return job.getState() == JobEnum.Doing;		
	}
	
	public Job getCurrentJob() {
		return job;
	}

	public void allocateJob(Job job) {
		if(job == null) return;
		job.getBlock().bind(this);
		job.setState(JobEnum.Doing);
		this.job = job;
	}
	
	public void work(){
		if(job.isNil())
			return;
		Command c = job.getCommand();
		Factory f = job.getFactory();
		Block b = job.getBlock();
		int abX = 0;
		int abY = 0;
		Queue<GPosition> bfsQueToFindEmpty = new LinkedList<GPosition>();
		
		if(c == Command.Bring){
			if(holding == null){
				// to block
				abX = Math.abs(b.getPos().getX()-this.getPos().getX());
				abY = Math.abs(b.getPos().getY()-this.getPos().getY());
				plusEmptyDis(abX, abY);
				holding = b;
				holding.bind(this);
				this.setPos(new GPosition(b.getPos().getX(), b.getPos().getY()));
				if(Dispatcher.Debug) System.out.println("Transporter "+this+" loaded  Block "+holding+".");
				
				// to factory
				if(f.isHoldBlock()){
					// the factory already has BLOCK.
					bfsQueToFindEmpty.add(f.getPos());
					GPosition newPos = findEmptyTile(f.getPos(), bfsQueToFindEmpty);
					if(newPos == null){
						System.out.println("There is no space to put Block.");
						return;
					}
					holding.setPos(newPos);				
					holding.release();
					holding = null;
					if(Dispatcher.Debug) System.out.println("Transporter "+this+" put  Block "+b+" near Factory "+f+".");
				}else{
					// the factory has no BLOCK.
					abX += Math.abs(f.getPos().getX()-this.getPos().getX());
					abY += Math.abs(f.getPos().getY()-this.getPos().getY());
					holding.setPos(new GPosition(f.getPos().getX(), f.getPos().getY()));
					holding.release();
					holding = null;
					this.setPos(new GPosition(f.getPos().getX(), f.getPos().getY()));
					if(Dispatcher.Debug) System.out.println("Transporter "+this+" put  Block "+b+" at Factory "+f+".");
				}
			}
		}else if(c == Command.Remove){
			if(holding == null){
				// to factory
				abX = Math.abs(f.getPos().getX()-this.getPos().getX());
				abY = Math.abs(f.getPos().getY()-this.getPos().getY());
				plusEmptyDis(abX, abY);
				holding = b;
				holding.bind(this);
				this.setPos(new GPosition(f.getPos().getX(), f.getPos().getY()));
				if(Dispatcher.Debug) System.out.println("Transporter "+this+" moves to Factory "+f+"  to load Block "+b+".");
				
				// to anything to throw out the block
				bfsQueToFindEmpty.add(f.getPos());
				GPosition newPos = findEmptyTile(f.getPos(), bfsQueToFindEmpty);
				if(newPos == null){
					System.out.println("There is no space to put Block.");
					return;
				}
				holding.setPos(newPos);				
				holding.release();
				holding = null;
				if(Dispatcher.Debug) System.out.println("Transporter "+this+" put  Block "+b+" near Factory "+f+".");
			}
		}
		
		plusTotalDis(abX, abY);
		job = new Job(null, null, null);
		job.setState(JobEnum.Done);
	}
	
	private GPosition findEmptyTile(GPosition targetPos, Queue<GPosition> q){
		while(!q.isEmpty()){
			GPosition pos = q.remove();
			TileEnum state = map[pos.getY()][pos.getX()].getStuff().getState(); 
			switch (state) {
			case EMPTY:
				return pos;
			case IMPOSSIBLE:
				break;
			default:
				for(int i=0; i<wayAlgorithm.length; i++){
					q.add(targetPos.combine(wayAlgorithm[i]));
				}
				break;
			}
		}
		return null;
	}
}
